package com.filldream.fastboot.common.util;


import com.alibaba.fastjson.JSONObject;
import com.filldream.fastboot.common.exception.GlobalException;
import com.filldream.fastboot.common.entity.ErrorCode;
import com.filldream.fastboot.common.util.entity.wx.WxUser;
import com.filldream.fastboot.common.util.entity.wx.Jscode2SessionDTO;
import com.filldream.fastboot.common.util.entity.wx.WxInitSTO;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class WxUtil {

    /**
     * 自动获取用户信息
     * @param wxInitSTO
     * @return
     */
    public static WxUser getWxUser( WxInitSTO wxInitSTO ){
        Integer loginType = wxInitSTO.getLoginType();
        try {
            if (loginType.equals(1)) {
                return decodeWxUser(wxInitSTO.getCode(), wxInitSTO.getEncryptedData(), wxInitSTO.getIv(), wxInitSTO.getAppId(), wxInitSTO.getAppSecret());
            } else if (loginType.equals(2)) {
                return getH5WxUser(wxInitSTO);
            }
        }catch (Exception e){
            throw new GlobalException(ErrorCode.WECHAT_USERINFO_ERR.reMsg(),e);
        }
        return null;
    }


    /**
     * 通过code换取网页授权(H5)access_token以及openID
     * https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html#0
     * @param appid
     * @param secret
     * @param code
     * @return
     * @throws IOException
     * result:{"access_token":"ACCESS_TOKEN","expires_in":7200,"refresh_token":"REFRESH_TOKEN","openid":"OPENID","scope":"SCOPE"}
     */
    public static String wxLogin(String appid, String secret,String code) throws IOException{
        String url=
                "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid
                        + "&secret="+ secret
                        +"&code="+ code
                        +"&grant_type=authorization_code";
        String s = UrlUtil.doGet(url);
        if( StringUtils.isEmpty(s) ){
            throw new IOException("WxLogin code " + s);
        }
        return s;
    }


    /**
     * H5获取微信用户信息信息
     * https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html#0
     * @param accessToken
     * @param openId
     * @return
     * @throws IOException
     * result:{"openid":" OPENID","nickname":"NICKNAME","sex":"1","province":"PROVINCE","city":"CITY","country":"COUNTRY","headimgurl":"https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46","privilege":["PRIVILEGE1","PRIVILEGE2"],"unionid":"o6_bmasdasdsad6_2sgVt7hMZOPfL"}
     */
    public static String wxInfo(String accessToken, String openId) throws IOException{
        String url= "https://api.weixin.qq.com/sns/userinfo?access_token="+ accessToken
                +"&openid="+ openId
                +"&lang=zh_CN";
        String s = UrlUtil.doGet(url);
        if( StringUtils.isEmpty(s) ){
            throw new IOException("WxInfo code " + s);
        }
        return s;
    }


    /**
     * H5获取用户信息
     * @param wxInitSTO
     * @return
     */
    public static WxUser getH5WxUser( WxInitSTO wxInitSTO ){
        WxUser user = new WxUser();
        try {
            String json = wxLogin(wxInitSTO.getAppId(), wxInitSTO.getAppSecret(), wxInitSTO.getCode());
            Map<String,Object> map = JsonUtil.fromJson(json,HashMap.class);

            //获取AccessToken
            if(!map.containsKey("openid")){
                //openid获取失败
                throw new GlobalException(ErrorCode.WECHAT_OPENID_ERR.reMsg());
            }

            if(!map.containsKey("unionid")){
                String unionid = (String)map.get("unionid");
                user.setUnionid(unionid);
            }

            String accessToken = (String)map.get("access_token");
            String openId = (String)map.get("openid");
            Assert.isEmpty(accessToken,ErrorCode.WECHAT_ACCESSTOKEN_ERR.reMsg());

            String wxInfo = WxUtil.wxInfo(accessToken, openId);
            Map<String,Object> wxInfoMap = JsonUtil.fromJson(wxInfo,HashMap.class);
            //根据access_token获取用户的信息
            String nickName = (String)wxInfoMap.get("nickname");
            int sex =(int) wxInfoMap.get("sex");
            String headImg =(String) wxInfoMap.get("headimgurl");

            user.setNickname(nickName);
            user.setSex(sex);
            user.setHeadimgurl(headImg);
            user.setAppId( wxInitSTO.getAppId() );
        } catch (IOException e) {
            throw new GlobalException(ErrorCode.WECHAT_USERINFO_ERR.reMsg(),e);
        }
        return user;
    }

    /**
     * 解密小游戏小程序用户信息
     * @param code
     * @param encryptedData
     * @param iv
     * @param appId
     * @param secret
     * @return
     * @throws IOException
     */
    public static WxUser decodeWxUser(String code, String encryptedData, String iv, String appId, String secret) throws IOException {
        WxUser wxUser = new WxUser();
        Assert.isEmpty(ErrorCode.BASE_ERR.reMsg("参数不齐"),code,encryptedData,iv);

        //向微信服务器 使用登录凭证 code 获取 session_key 和 openid
        Jscode2SessionDTO j2s = doJscode2session(appId, secret, code, "authorization_code");
        //获取会话密钥（session_key）
        String sessionKey = j2s.getSession_key();
        //用户的唯一标识（openid）
        String openid = j2s.getOpenid();
        String unionid = j2s.getUnionid();
        wxUser.setOpenid(openid);
        wxUser.setUnionid(unionid);
        wxUser.setSessionKey( sessionKey );

        if(StringUtils.isNotBlank(encryptedData) && StringUtils.isNotBlank(iv)){
            //对encryptedData加密数据进行AES解密
            try {
                String result = WxAesUtil.decrypt(encryptedData, sessionKey, iv, "UTF-8");
                if (null != result && result.length() > 0) {
                    JSONObject userInfoJSON = JSONObject.parseObject(result);
                    wxUser.setOpenid( userInfoJSON.getString("openId") );
                    wxUser.setNickname( userInfoJSON.getString("nickName") );
                    wxUser.setSex( userInfoJSON.getInteger("gender") );
                    wxUser.setCity( userInfoJSON.getString("city") );
                    wxUser.setProvince( userInfoJSON.getString("province") );
                    wxUser.setCountry( userInfoJSON.getString("country") );
                    wxUser.setHeadimgurl( userInfoJSON.getString("avatarUrl") );
                    wxUser.setUnionid( userInfoJSON.getString("unionId") );
                    return wxUser;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return wxUser;
    }

    /**
     * 小程序、小游戏获取openid、session_key
     * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
     * @param appId
     * @param secret
     * @param code
     * @param grant_type
     * @return
     */
    public static Jscode2SessionDTO doJscode2session(String appId, String secret, String code, String grant_type){
        Map<String,String> params = new HashMap<>();
        params.put("appid",appId);
        params.put("secret",secret);
        params.put("js_code",code);
        params.put("grant_type",grant_type); //默认authorization_code
        try {
            String sr = UrlUtil.doGet("https://api.weixin.qq.com/sns/jscode2session", params);
            return JsonUtil.fromJson(sr,Jscode2SessionDTO.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    /** ----------辅助方法---------- **/

    public static String createNonceStr() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    public static String createTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

    public static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

}
